/********************************************************************
	File :			ChannelFactory.cpp
	Creation date :	2010/6/27
		
	License :			Copyright 2010 Ahmed Charfeddine, http://www.pushframework.com

				   Licensed under the Apache License, Version 2.0 (the "License");
				   you may not use this file except in compliance with the License.
				   You may obtain a copy of the License at
				
					   http://www.apache.org/licenses/LICENSE-2.0
				
				   Unless required by applicable law or agreed to in writing, software
				   distributed under the License is distributed on an "AS IS" BASIS,
				   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
				   See the License for the specific language governing permissions and
				   limitations under the License.
	
	
*********************************************************************/
#include "StdAfx.h"
#include "ChannelFactory.h"

#include "ServerImpl.h"
#include "IOCPQueue.h"
#include "Dispatcher.h"

#include "Listener.h"

#include "ScopedLock.h"


namespace PushFramework{


ChannelFactory::ChannelFactory(ServerImpl* pServerImpl)
{
	this->pServerImpl = pServerImpl;
	::InitializeCriticalSection(&csChannelMap);
	
	uLoginExpireDuration = 40;
	nMaxConnections = 100;

	isPermitOnly = false;
}

ChannelFactory::~ChannelFactory(void)
{
	for (channelMapT::iterator it=channelMap.begin();
		it!=channelMap.end();
		it++
		)
	{
		CChannel* pChannel = it->second;
		delete pChannel;
	}

	::DeleteCriticalSection(&csChannelMap);
}

void ChannelFactory::addObject( CChannel* pChannel )
{
	ScopedLock lock(csChannelMap);
	
	channelMap[pChannel->getKey()] = pChannel;
}

CChannel* ChannelFactory::getObject( CChannel::Key key )
{
	ScopedLock lock(csChannelMap);
	
	channelMapT::iterator it = channelMap.find(key);
	if (it == channelMap.end())
		return NULL;

	CChannel* pChannel = it->second;
	pChannel->getRefCount()++;
	return pChannel;
}

void ChannelFactory::disposeObject( CChannel::Key key )
{
	ScopedLock lock(csChannelMap);

	channelMapT::iterator it = channelMap.find(key);
	if (it == channelMap.end())
		return;

	CChannel* pChannel = it->second;
	pChannel->getRefCount()--;

	if (pChannel->getRefCount()== 0 && pChannel->getStatus()==CChannel::Released)
	{
		disposeChannel(pChannel);
		channelMap.erase(it);
	}
}

void ChannelFactory::removeObject( CChannel::Key key )
{
	ScopedLock lock(csChannelMap);

	channelMapT::iterator it = channelMap.find(key);
	if (it == channelMap.end())
		return;

	CChannel* pChannel = it->second;
	
	disposeChannel(pChannel);

	channelMap.erase(it);
}

void ChannelFactory::closeNonLogged()
{
	wcout << L"closeNonLogged"<<std::endl;
        return;
	ScopedLock lock(csChannelMap);

	for (channelMapT::iterator it=channelMap.begin();
		it!=channelMap.end();
		)
	{
		CChannel* pChannel = it->second;
		if (pChannel->getStatus()==CChannel::Connected)
		{
                    double maxAllowedJourney = pChannel->isObserverChannel() ? 40 : uLoginExpireDuration;
			if (pChannel->getLifeDuration() > maxAllowedJourney)
			{
				wcout << L"Disconnecting an unlogged channel"<<std::endl;
				pChannel->Close(false);
				if (pChannel->getRefCount() == 0)
				{
					disposeChannel(pChannel);
					/*it =*/ channelMap.erase(it);
					continue;
				}				
			}						
		}
		it++;
	}	
}

void ChannelFactory::BroadcastToMonitors( OutgoingPacket* pPacket )
{
	ScopedLock lock(csChannelMap);

	for (channelMapT::iterator it=channelMap.begin();
		it!=channelMap.end();
		it++
		)
	{
		CChannel* pChannel = it->second;
		if (pChannel->isObserverChannel() &&
			pChannel->getStatus()==CChannel::Attached)
		{
			pChannel->PushPacket(pPacket);					
		}
	}
}

void ChannelFactory::ProcessPendingObserversPackets()
{
	ScopedLock lock(csChannelMap);

	for (channelMapT::iterator it=channelMap.begin();
		it!=channelMap.end();
		it++
		)
	{
		CChannel* pChannel = it->second;
		if (pChannel->isObserverChannel() &&
			pChannel->getStatus()==CChannel::Attached)
		{
			pServerImpl->getDispatcher()->ProcessClientPendingPackets(pChannel);
		}
	}
}

void ChannelFactory::ProcessPendingClientPackets( std::string broadcastChannel )
{
	ScopedLock lock(csChannelMap);

	for (channelMapT::iterator it=channelMap.begin();
		it!=channelMap.end();
		it++
		)
	{
		CChannel* pChannel = it->second;
		if (!pChannel->isObserverChannel() &&
			pChannel->getStatus()==CChannel::Attached)
		{
			pServerImpl->getDispatcher()->ProcessClientPendingPackets(pChannel);
		}
	}
}

void ChannelFactory::disposeChannel( CChannel* pChannel )
{
	pChannel->unintialize();
	//
	delete pChannel;
		
}

void ChannelFactory::setLoginExpiryDuration( unsigned int uLoginExpireDuration )
{
	this->uLoginExpireDuration = uLoginExpireDuration;
}

void ChannelFactory::setMaxAllowedConnections( unsigned int nMaxConnections )
{
	this->nMaxConnections = nMaxConnections;
}

bool ChannelFactory::SetupPhysicalConnection( SOCKET hSocket, ListenerOptions* pListenerOptions, SOCKADDR_IN address, bool isObserver )
{
	CChannel* pChannel = new CChannel(pServerImpl, pListenerOptions);

	
	pChannel->initialize(hSocket, address, isObserver);

	//Now Associate with IOCP main Handle
	if (!pServerImpl->getIOCPQueue()->AddSocketContext(hSocket, (void*) pChannel))
	{
		disposeChannel(pChannel);
		//leave socket close to acceptor
		return false;
	}

	addObject(pChannel);

        pServerImpl->getDispatcher()->OnInitializeReady(pChannel);
	return true;
}

bool ChannelFactory::SetupClientConnection( SOCKET hSocket, SOCKADDR_IN address, Listener* pListener )
{
	//Check if max connections is not exceeded.
	if (channelMap.size() >= nMaxConnections)
	{
		return false;
	}

	//Check if address is not blocked :
	if (!isNotBlockedAddress(address.sin_addr))
	{
		return false;
	}

	//
	return SetupPhysicalConnection(hSocket, &pListener->getOptions(), address, false);
}

bool ChannelFactory::SetupDashboardConnection( SOCKET hSocket, SOCKADDR_IN address, Listener* pListener )
{
	return SetupPhysicalConnection(hSocket, &pListener->getOptions(), address, true);
}

bool ChannelFactory::isNotBlockedAddress( IN_ADDR SockAddr )
{

	long ip = 0;

	//ip = SockAddr.S_un.S_un_b.s_b1 * 16777216 + SockAddr.S_un.S_un_b.s_b2 * 65536 + 	SockAddr.S_un.S_un_b.s_b3 * 256 + 	SockAddr.S_un.S_un_b.s_b4;

	bool bAllow = false;

	ipRangeListT& list = isPermitOnly ? permittedIPs : blockedIPs;
	bool bIsInList = false;

	for (int i=0;i<list.size();i++)
	{
		IPRange& ipRange = list.at(i);

		long start = 0;
		long stop = 0;

		/*start = ipRange.startIP.S_un.S_un_b.s_b1 * 16777216 + ipRange.startIP.S_un.S_un_b.s_b2 * 65536 + 	ipRange.startIP.S_un.S_un_b.s_b3 * 256 + 	ipRange.startIP.S_un.S_un_b.s_b4;
		stop = ipRange.endIP.S_un.S_un_b.s_b1 * 16777216 + ipRange.endIP.S_un.S_un_b.s_b2 * 65536 + 	ipRange.endIP.S_un.S_un_b.s_b3 * 256 + 	ipRange.endIP.S_un.S_un_b.s_b4;
*/

		if (ip <= stop && ip >= start)
		{
			bIsInList = true;
			break;
		}
	}
	return isPermitOnly ? bIsInList : !bIsInList;
}

void ChannelFactory::addIPRangeAccess( const char* ipStart, const char* ipStop, bool bPermit )
{
	isPermitOnly = bPermit;

	IPRange ipRange;

	/*
	ipRange.startIP = inet_addr(ipStart);
		ipRange.endIP = inet_addr(ipStop);*/
	

	ipRangeListT& list = bPermit ? permittedIPs : blockedIPs;

	list.push_back(ipRange);
}

}
